<!DOCTYPE html>
<html lang="zh" dir="ltr" class="client-nojs">
<head>
<title>协程 (C++20)</title>
<meta charset="UTF-8">
<meta name="generator" content="MediaWiki 1.21.2">
<link rel="shortcut icon" href="../../../common/favicon.ico">
<link rel="stylesheet" href="../../../common/ext.css">
<meta name="ResourceLoaderDynamicStyles" content="">
<link rel="stylesheet" href="../../../common/site_modules.css">
<style>a:lang(ar),a:lang(ckb),a:lang(fa),a:lang(kk-arab),a:lang(mzn),a:lang(ps),a:lang(ur){text-decoration:none}#toc{display:none}.editsection{display:none}
/* cache key: mwiki1-mwiki_zh_:resourceloader:filter:minify-css:7:15cea3ec788a65b5187d4018eed543bf */</style>

<script src="../../../common/startup_scripts.js"></script>
<script>if(window.mw){
mw.config.set({"wgCanonicalNamespace":"","wgCanonicalSpecialPageName":false,"wgNamespaceNumber":0,"wgPageName":"cpp/language/coroutines","wgTitle":"cpp/language/coroutines","wgCurRevisionId":71917,"wgArticleId":12015,"wgIsArticle":true,"wgAction":"view","wgUserName":null,"wgUserGroups":["*"],"wgCategories":["Todo with reason"],"wgBreakFrames":false,"wgPageContentLanguage":"zh","wgSeparatorTransformTable":["",""],"wgDigitTransformTable":["",""],"wgDefaultDateFormat":"zh","wgMonthNames":["","1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"wgMonthNamesShort":["","1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"wgRelevantPageName":"cpp/language/coroutines","wgUserVariant":"zh","wgRestrictionEdit":[],"wgRestrictionMove":[]});
}</script><script>if(window.mw){
mw.loader.implement("user.options",function(){mw.user.options.set({"ccmeonemails":0,"cols":80,"date":"default","diffonly":0,"disablemail":0,"disablesuggest":0,"editfont":"default","editondblclick":0,"editsection":0,"editsectiononrightclick":0,"enotifminoredits":0,"enotifrevealaddr":0,"enotifusertalkpages":1,"enotifwatchlistpages":0,"extendwatchlist":0,"externaldiff":0,"externaleditor":0,"fancysig":0,"forceeditsummary":0,"gender":"unknown","hideminor":0,"hidepatrolled":0,"imagesize":2,"justify":0,"math":1,"minordefault":0,"newpageshidepatrolled":0,"nocache":0,"noconvertlink":0,"norollbackdiff":0,"numberheadings":0,"previewonfirst":0,"previewontop":1,"quickbar":5,"rcdays":7,"rclimit":50,"rememberpassword":0,"rows":25,"searchlimit":20,"showhiddencats":0,"showjumplinks":1,"shownumberswatching":1,"showtoc":0,"showtoolbar":1,"skin":"cppreference2","stubthreshold":0,"thumbsize":2,"underline":2,"uselivepreview":0,"usenewrc":0,"watchcreations":0,"watchdefault":0,"watchdeletion":0,
"watchlistdays":3,"watchlisthideanons":0,"watchlisthidebots":0,"watchlisthideliu":0,"watchlisthideminor":0,"watchlisthideown":0,"watchlisthidepatrolled":0,"watchmoves":0,"wllimit":250,"variant":"zh","language":"zh","searchNs0":true,"searchNs1":false,"searchNs2":false,"searchNs3":false,"searchNs4":false,"searchNs5":false,"searchNs6":false,"searchNs7":false,"searchNs8":false,"searchNs9":false,"searchNs10":false,"searchNs11":false,"searchNs12":false,"searchNs13":false,"searchNs14":false,"searchNs15":false,"gadget-MathJax":1,"gadget-ColiruCompiler":1});;},{},{});mw.loader.implement("user.tokens",function(){mw.user.tokens.set({"editToken":"+\\","patrolToken":false,"watchToken":false});;},{},{});
/* cache key: mwiki1-mwiki_zh_:resourceloader:filter:minify-js:7:258d7cd6aa9aa67dee25e01fb6a9e505 */
}</script>
<script>if(window.mw){
mw.loader.load(["mediawiki.page.startup","mediawiki.legacy.wikibits","mediawiki.legacy.ajax"]);
}</script>
<style type="text/css">/*<![CDATA[*/
.source-cpp {line-height: normal;}
.source-cpp li, .source-cpp pre {
	line-height: normal; border: 0px none white;
}
/**
 * GeSHi Dynamically Generated Stylesheet
 * --------------------------------------
 * Dynamically generated stylesheet for cpp
 * CSS class: source-cpp, CSS id: 
 * GeSHi (C) 2004 - 2007 Nigel McNie, 2007 - 2008 Benny Baumann
 * (http://qbnz.com/highlighter/ and http://geshi.org/)
 * --------------------------------------
 */
.cpp.source-cpp .de1, .cpp.source-cpp .de2 {font: normal normal 1em/1.2em monospace; margin:0; padding:0; background:none; vertical-align:top;}
.cpp.source-cpp  {font-family:monospace;}
.cpp.source-cpp .imp {font-weight: bold; color: red;}
.cpp.source-cpp li, .cpp.source-cpp .li1 {font-weight: normal; vertical-align:top;}
.cpp.source-cpp .ln {width:1px;text-align:right;margin:0;padding:0 2px;vertical-align:top;}
.cpp.source-cpp .li2 {font-weight: bold; vertical-align:top;}
.cpp.source-cpp .kw1 {color: #0000dd;}
.cpp.source-cpp .kw2 {color: #0000ff;}
.cpp.source-cpp .kw3 {color: #0000dd;}
.cpp.source-cpp .kw4 {color: #0000ff;}
.cpp.source-cpp .co1 {color: #909090;}
.cpp.source-cpp .co2 {color: #339900;}
.cpp.source-cpp .coMULTI {color: #ff0000; font-style: italic;}
.cpp.source-cpp .es0 {color: #008000; font-weight: bold;}
.cpp.source-cpp .es1 {color: #008000; font-weight: bold;}
.cpp.source-cpp .es2 {color: #008000; font-weight: bold;}
.cpp.source-cpp .es3 {color: #008000; font-weight: bold;}
.cpp.source-cpp .es4 {color: #008000; font-weight: bold;}
.cpp.source-cpp .es5 {color: #008000; font-weight: bold;}
.cpp.source-cpp .br0 {color: #008000;}
.cpp.source-cpp .sy0 {color: #008000;}
.cpp.source-cpp .sy1 {color: #000080;}
.cpp.source-cpp .sy2 {color: #000040;}
.cpp.source-cpp .sy3 {color: #000040;}
.cpp.source-cpp .sy4 {color: #008080;}
.cpp.source-cpp .st0 {color: #008000;}
.cpp.source-cpp .nu0 {color: #000080;}
.cpp.source-cpp .nu6 {color: #000080;}
.cpp.source-cpp .nu8 {color: #000080;}
.cpp.source-cpp .nu12 {color: #000080;}
.cpp.source-cpp .nu16 {color:#000080;}
.cpp.source-cpp .nu17 {color:#000080;}
.cpp.source-cpp .nu18 {color:#000080;}
.cpp.source-cpp .nu19 {color:#000080;}
.cpp.source-cpp .ln-xtra, .cpp.source-cpp li.ln-xtra, .cpp.source-cpp div.ln-xtra {background-color: #ffc;}
.cpp.source-cpp span.xtra { display:block; }

/*]]>*/
</style><style type="text/css">/*<![CDATA[*/
.source-text {line-height: normal;}
.source-text li, .source-text pre {
	line-height: normal; border: 0px none white;
}
/**
 * GeSHi Dynamically Generated Stylesheet
 * --------------------------------------
 * Dynamically generated stylesheet for text
 * CSS class: source-text, CSS id: 
 * GeSHi (C) 2004 - 2007 Nigel McNie, 2007 - 2008 Benny Baumann
 * (http://qbnz.com/highlighter/ and http://geshi.org/)
 * --------------------------------------
 */
.text.source-text .de1, .text.source-text .de2 {font: normal normal 1em/1.2em monospace; margin:0; padding:0; background:none; vertical-align:top;}
.text.source-text  {font-family:monospace;}
.text.source-text .imp {font-weight: bold; color: red;}
.text.source-text li, .text.source-text .li1 {font-weight: normal; vertical-align:top;}
.text.source-text .ln {width:1px;text-align:right;margin:0;padding:0 2px;vertical-align:top;}
.text.source-text .li2 {font-weight: bold; vertical-align:top;}
.text.source-text .ln-xtra, .text.source-text li.ln-xtra, .text.source-text div.ln-xtra {background-color: #ffc;}
.text.source-text span.xtra { display:block; }

/*]]>*/
</style><!--[if lt IE 7]><style type="text/css">body{behavior:url("/mwiki/skins/cppreference2/csshover.min.htc")}</style><![endif]--></head>
<body class="mediawiki ltr sitedir-ltr ns-0 ns-subject page-cpp_language_coroutines skin-cppreference2 action-view cpp-navbar">
        <!-- header -->
        <!-- /header -->
        <!-- content -->
<div id="cpp-content-base">
            <div id="content">
                <a id="top"></a>
                <div id="mw-js-message" style="display:none;"></div>
                                <!-- firstHeading -->
<h1 id="firstHeading" class="firstHeading">协程 <span class="t-mark-rev t-since-cxx20">(C++20)</span></h1>
                <!-- /firstHeading -->
                <!-- bodyContent -->
                <div id="bodyContent">
                                        <!-- tagline -->
                    <div id="siteSub">来自cppreference.com</div>
                    <!-- /tagline -->
                                        <!-- subtitle -->
                    <div id="contentSub"><span class="subpages">&lt; <a href="../../cpp.html" title="cpp">cpp</a>‎ | <a href="../language.html" title="cpp/language">language</a></span></div>
                    <!-- /subtitle -->
                                                            <!-- bodycontent -->
                    <div id="mw-content-text" lang="zh" dir="ltr" class="mw-content-ltr"><div class="t-navbar" style=""><div class="t-navbar-sep"> </div><div class="t-navbar-head"><a href="../../cpp.html" title="cpp"> C++</a><div class="t-navbar-menu"><div><div><table class="t-nv-begin" cellpadding="0" style="line-height:1.1em">
<tr class="t-nv"><td colspan="5"> <a href="../language.html" title="cpp/language">语言</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../header.html" title="cpp/header">标准库头文件</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../freestanding.html" title="cpp/freestanding"> 自立与有宿主实现</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../named_req.html" title="cpp/named req">具名要求</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../utility.html#.E8.AF.AD.E8.A8.80.E6.94.AF.E6.8C.81" title="cpp/utility">语言支持库</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../concepts.html" title="cpp/concepts">概念库</a> <span class="t-mark-rev t-since-cxx20">(C++20)</span> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../error.html" title="cpp/error">诊断库</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../utility.html" title="cpp/utility">工具库</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../string.html" title="cpp/string">字符串库</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../container.html" title="cpp/container">容器库</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../iterator.html" title="cpp/iterator">迭代器库</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../ranges.html" title="cpp/ranges"> 范围库</a> <span class="t-mark-rev t-since-cxx20">(C++20)</span> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../algorithm.html" title="cpp/algorithm">算法库</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../numeric.html" title="cpp/numeric">数值库</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../locale.html" title="cpp/locale">本地化库</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../io.html" title="cpp/io">输入/输出库</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../filesystem.html" title="cpp/filesystem">文件系统库</a> <span class="t-mark-rev t-since-cxx17">(C++17)</span> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../regex.html" title="cpp/regex">正则表达式库</a> <span class="t-mark-rev t-since-cxx11">(C++11)</span> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../atomic.html" title="cpp/atomic">原子操作库</a> <span class="t-mark-rev t-since-cxx11">(C++11)</span> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../thread.html" title="cpp/thread">线程支持库</a> <span class="t-mark-rev t-since-cxx11">(C++11)</span> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../experimental.html" title="cpp/experimental">技术规范</a> </td></tr>
</table></div><div></div></div></div></div><div class="t-navbar-sep"> </div><div class="t-navbar-head"><a href="../language.html" title="cpp/language">C++ 语言</a></div><div class="t-navbar-sep"> </div><div class="t-navbar-head"><a href="functions.html" title="cpp/language/functions"> 函数</a><div class="t-navbar-menu"><div><div style="display:inline-block">
<div><table class="t-nv-begin" cellpadding="0" style="line-height:1.1em">
<tr class="t-nv-h2"><td colspan="5"> 声明 </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="function.html" title="cpp/language/function"> 函数声明</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="function.html#.E5.BD.A2.E5.8F.82.E5.88.97.E8.A1.A8" title="cpp/language/function"> 函数形参列表</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="function.html#.E5.87.BD.E6.95.B0.E5.AE.9A.E4.B9.89" title="cpp/language/function"> 函数定义</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="default_arguments.html" title="cpp/language/default arguments"> 默认实参</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="variadic_arguments.html" title="cpp/language/variadic arguments"> 变长实参</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="inline.html" title="cpp/language/inline"> inline 说明符</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="lambda.html" title="cpp/language/lambda"> lambda 表达式</a> <span class="t-mark-rev t-since-cxx11">(C++11)</span></td></tr>
<tr class="t-nv"><td colspan="5"> <strong class="selflink"> 协程</strong> <span class="t-mark-rev t-since-cxx20">(C++20)</span></td></tr>
<tr class="t-nv-h2"><td colspan="5"> 函数调用 </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="adl.html" title="cpp/language/adl"> 实参依赖查找 (ADL)</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="operator_other.html#.E5.86.85.E5.BB.BA.E7.9A.84.E5.87.BD.E6.95.B0.E8.B0.83.E7.94.A8.E8.BF.90.E7.AE.97.E7.AC.A6" title="cpp/language/operator other"> 函数调用运算符</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="../named_req/FunctionObject.html" title="cpp/named req/FunctionObject"> 函数对象</a> </td></tr>
<tr class="t-nv-h2"><td colspan="5"> 重载 </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="overload_resolution.html" title="cpp/language/overload resolution"> 重载决议</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="operators.html" title="cpp/language/operators"> 运算符重载</a> </td></tr>
<tr class="t-nv"><td colspan="5"> <a href="overloaded_address.html" title="cpp/language/overloaded address"> 重载集地址</a> </td></tr>
</table></div>
</div><div></div></div></div></div><div class="t-navbar-sep"> </div></div>
<p>协程是能暂停执行以在之后恢复的函数。协程是无栈的：它们通过返回到调用方暂停执行，并且从栈分离存储恢复执行需要的数据。这样就可以编写异步执行的顺序代码（例如不使用显式的回调来处理非阻塞 I/O），还支持对惰性计算的无限序列上的算法及其他用途。
</p><p>如果函数的定义进行了下列操作之一，那么它是协程：
</p>
<ul><li> 用 <code>co_await</code> 运算符暂停执行，直到恢复：
</li></ul>
<div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="cpp source-cpp"><pre class="de1">task<span class="sy1">&lt;&gt;</span> tcp_echo_server<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
  <span class="kw4">char</span> data<span class="br0">[</span><span class="nu0">1024</span><span class="br0">]</span><span class="sy4">;</span>
  <span class="kw1">while</span> <span class="br0">(</span><span class="kw2">true</span><span class="br0">)</span> <span class="br0">{</span>
    <a href="../types/size_t.html"><span class="kw100">std::<span class="me2">size_t</span></span></a> n <span class="sy1">=</span> co_await socket.<span class="me1">async_read_some</span><span class="br0">(</span>buffer<span class="br0">(</span>data<span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
    co_await async_write<span class="br0">(</span>socket, buffer<span class="br0">(</span>data, n<span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span>
  <span class="br0">}</span>
<span class="br0">}</span></pre></div></div>
<ul><li> 用关键词 <code>co_yield</code> 暂停执行并返回一个值：
</li></ul>
<div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="cpp source-cpp"><pre class="de1">generator<span class="sy1">&lt;</span><span class="kw4">int</span><span class="sy1">&gt;</span> iota<span class="br0">(</span><span class="kw4">int</span> n <span class="sy1">=</span> <span class="nu0">0</span><span class="br0">)</span> <span class="br0">{</span>
  <span class="kw1">while</span><span class="br0">(</span><span class="kw2">true</span><span class="br0">)</span>
    co_yield n<span class="sy2">++</span><span class="sy4">;</span>
<span class="br0">}</span></pre></div></div>
<ul><li> 用关键词 <span class="t-lc">co_return</span> 完成执行并返回一个值：
</li></ul>
<div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="cpp source-cpp"><pre class="de1">lazy<span class="sy1">&lt;</span><span class="kw4">int</span><span class="sy1">&gt;</span> f<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
  co_return <span class="nu0">7</span><span class="sy4">;</span>
<span class="br0">}</span></pre></div></div>
<p>每个协程必须具有能够满足一组要求的返回类型，标注于下。
</p>
<h3><span class="mw-headline" id=".E9.99.90.E5.88.B6"> 限制 </span></h3>
<p>协程不能使用<a href="variadic_arguments.html" title="cpp/language/variadic arguments">变长实参</a>，普通的 <a href="return.html" title="cpp/language/return">return</a> 语句，或<a href="function.html" title="cpp/language/function">占位符返回类型</a>（<a href="auto.html" title="cpp/language/auto"><code>auto</code></a> 或 <code>Concept</code>）。
</p><p><a href="constexpr.html" title="cpp/language/constexpr">constexpr 函数</a>、<a href="initializer_list.html" title="cpp/language/constructor">构造函数</a>、<a href="destructor.html" title="cpp/language/destructor">析构函数</a>及 <a href="main_function.html" title="cpp/language/main function"><code>main</code> 函数</a> 不能是协程。
</p>
<h3><span class="mw-headline" id=".E6.89.A7.E8.A1.8C"> 执行 </span></h3>
<p>每个协程均与下列对象关联：
</p>
<ul><li> <i>承诺（promise）对象</i>，从协程内部操纵。协程通过此对象提交其结果或异常。
</li><li> <i>协程句柄 (coroutine handle)</i>，从协程外部操纵。这是用于恢复协程执行或销毁协程帧的非拥有柄。
</li><li> <i>协程状态 (coroutine state)</i>，它是一个包含以下各项的分配于堆（除非优化掉其分配）的内部对象：
</li></ul>
<dl><dd><ul><li> 承诺对象
</li><li> 各个形参（全部按值复制）
</li><li> 当前暂停点的某种表示，使得恢复时程序知晓要从何处继续，销毁时知晓有哪些局部变量在作用域内
</li><li> 生存期跨过当前暂停点的局部变量和临时量
</li></ul>
</dd></dl>
<p>当协程开始执行时，它进行下列操作：
</p>
<ul><li> 用 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="../memory/new/operator_new.html"><span class="kw687">operator new</span></a></span></span> 分配协程状态对象（见下文）
</li><li> 将所有函数形参复制到协程状态中：按值传递的形参被移动或复制，按引用传递的参数保持为引用（因此，如果在被指代对象的生存期结束后恢复协程，它可能变成悬垂引用）
</li><li> 调用承诺对象的构造函数。如果承诺类型拥有接收所有协程形参的构造函数，那么以复制后的协程实参调用该构造函数。否则调用其默认构造函数。
</li><li> 调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">promise.<span class="me1">get_return_object</span><span class="br0">(</span><span class="br0">)</span></span></span> 并将其结果在局部变量中保持。该调用的结果将在协程首次暂停时返回给调用方。至此并包含这个步骤为止，任何抛出的异常均传播回调用方，而非置于承诺中。
</li><li> 调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">promise.<span class="me1">initial_suspend</span><span class="br0">(</span><span class="br0">)</span></span></span> 并 <span class="t-c"><span class="mw-geshi cpp source-cpp">co_await</span></span> 其结果。典型的承诺类型要么（对于惰性启动的协程）返回 <span class="t-c"><span class="mw-geshi cpp source-cpp">std<span class="sy4">::</span><span class="me2">suspend_always</span></span></span>，要么（对于急切启动的协程）返回 <span class="t-c"><span class="mw-geshi cpp source-cpp">std<span class="sy4">::</span><span class="me2">suspend_never</span></span></span>。
</li><li> 当 <span class="t-c"><span class="mw-geshi cpp source-cpp">co_await promise.<span class="me1">initial_suspend</span><span class="br0">(</span><span class="br0">)</span></span></span> 恢复时，开始协程体的执行。
</li></ul>
<p>当协程抵达暂停点时：
</p>
<ul><li> 将先前获得的返回对象返回给调用方/恢复方，如果需要则先隐式转换到协程的返回类型。
</li></ul>
<p>当协程抵达 <span class="t-c"><span class="mw-geshi cpp source-cpp">co_return</span></span> 语句时，它进行下列操作：
</p>
<ul><li> 对下列情形调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">promise.<span class="me1">return_void</span><span class="br0">(</span><span class="br0">)</span></span></span>
</li></ul>
<dl><dd><ul><li> <span class="t-c"><span class="mw-geshi cpp source-cpp">co_return<span class="sy4">;</span></span></span>
</li><li> <span class="t-c"><span class="mw-geshi cpp source-cpp">co_return expr</span></span>，其中 expr 具有 void 类型
</li><li> 控制流出返回 void 的协程的结尾。此时如果承诺类型 Promise 没有 <span class="t-c"><span class="mw-geshi cpp source-cpp">Promise<span class="sy4">::</span><span class="me2">return_void</span><span class="br0">(</span><span class="br0">)</span></span></span> 成员函数，那么则行为未定义。
</li></ul>
</dd></dl>
<ul><li> 或对于 <span class="t-c"><span class="mw-geshi cpp source-cpp">co_return expr</span></span> 调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">promise.<span class="me1">return_value</span><span class="br0">(</span>expr<span class="br0">)</span></span></span>，其中 expr 具有非 void 类型
</li><li> 以创建的逆序销毁所有具有自动存储期的变量。
</li><li> 调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">promise.<span class="me1">final_suspend</span><span class="br0">(</span><span class="br0">)</span></span></span> 并 <span class="t-c"><span class="mw-geshi cpp source-cpp">co_await</span></span> 其结果。
</li></ul>
<p>如果协程因未捕捉的异常结束，那么它进行下列操作：
</p>
<ul><li> 捕捉异常并在 catch 块内调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">promise.<span class="me1">unhandled_exception</span><span class="br0">(</span><span class="br0">)</span></span></span>
</li><li> 调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">promise.<span class="me1">final_suspend</span><span class="br0">(</span><span class="br0">)</span></span></span> 并 <span class="t-c"><span class="mw-geshi cpp source-cpp">co_await</span></span> 其结果（例如，以恢复某个继续或发布其结果）。此时开始恢复协程是未定义行为。
</li></ul>
<p>当经由 <code>co_return</code> 或未捕捉异常而终止协程导致协程状态被销毁，或经由其句柄而导致其被销毁时，它进行下列操作：
</p>
<ul><li> 调用承诺对象的析构函数。
</li><li> 调用各个函数形参副本的析构函数。
</li><li> 调用 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="../memory/new/operator_delete.html"><span class="kw689">operator delete</span></a></span></span> 以释放协程状态所用的内存。
</li><li> 转移执行回到调用方/恢复方。
</li></ul>
<h3><span class="mw-headline" id=".E5.A0.86.E5.88.86.E9.85.8D"> 堆分配 </span></h3>
<p>协程状态由非数组 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="../memory/new/operator_new.html"><span class="kw687">operator new</span></a></span></span> 在堆上分配。
</p><p>如果承诺类型定义了类级别的替代函数，那么会使用它，否则会使用全局的 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="../memory/new/operator_new.html"><span class="kw687">operator new</span></a></span></span>。
</p><p>如果承诺类型定义了接收额外形参的 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="../memory/new/operator_new.html"><span class="kw687">operator new</span></a></span></span> 的布置形式，且它们所匹配的实参列表中的第一实参是要求的大小（<span class="t-lc"><a href="../types/size_t.html" title="cpp/types/size t">std::size_t</a></span> 类型），而其余则是各个协程函数实参，那么将这些实参传递给 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="../memory/new/operator_new.html"><span class="kw687">operator new</span></a></span></span>（这使得能对协程使用<a href="../memory/uses_allocator.html#.E4.BD.BF.E7.94.A8.E5.88.86.E9.85.8D.E5.99.A8.E6.9E.84.E9.80.A0" title="cpp/memory/uses allocator">前导分配器约定</a>）
</p><p>以下情况下，可以优化掉对 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="../memory/new/operator_new.html"><span class="kw687">operator new</span></a></span></span> 的调用（即使使用了自定义分配器）：
</p>
<ul><li> 协程状态的生存期严格内嵌于调用方的生存期，且
</li><li> 协程帧的大小在调用点已知
</li></ul>
<p>此时协程状态嵌入调用方的栈帧（如果调用方是普通函数）或协程状态（如果调用方是协程）之中。
</p><p>如果分配失败，那么协程抛出 <span class="t-lc"><a href="../memory/new/bad_alloc.html" title="cpp/memory/new/bad alloc">std::bad_alloc</a></span>，除非承诺类型 Promise 类型定义了成员函数 <span class="t-c"><span class="mw-geshi cpp source-cpp">Promise<span class="sy4">::</span><span class="me2">get_return_object_on_allocation_failure</span><span class="br0">(</span><span class="br0">)</span></span></span>。如果定义了该成员函数，那么使用 <span class="t-c"><span class="mw-geshi cpp source-cpp"><a href="../memory/new/operator_new.html"><span class="kw687">operator new</span></a></span></span> 的 <span class="t-c"><span class="mw-geshi cpp source-cpp">nothrow</span></span> 形式进行分配，而在分配失败时，协程会立即将从 <span class="t-c"><span class="mw-geshi cpp source-cpp">Promise<span class="sy4">::</span><span class="me2">get_return_object_on_allocation_failure</span><span class="br0">(</span><span class="br0">)</span></span></span> 获得的对象返回给调用方。
</p>
<h3><span class="mw-headline" id=".E6.89.BF.E8.AF.BA.E7.B1.BB.E5.9E.8B.EF.BC.88Promise.EF.BC.89"> 承诺类型（<span class="t-c"><span class="mw-geshi cpp source-cpp">Promise</span></span>） </span></h3>
<p>编译器用 <span class="t-lc">std::coroutine_traits</span> 从协程的返回类型确定承诺类型。
</p><p>正式而言，令 <code>R</code> 与 <code>Args...</code> 分别代表协程的返回类型与参数类型列表，<code>ClassT</code> 与 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="coMULTI">/*cv限定*/</span></span></span> （如果存在）分别代表协程所属的类与其 cv 限定，如果定义它为非静态成员函数，以如下方式确定它的承诺类型 ：
</p>
<ul><li> <span class="t-c"><span class="mw-geshi cpp source-cpp">std<span class="sy4">::</span><span class="me2">coroutine_traits</span><span class="sy1">&lt;</span>R, Args...<span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">promise_type</span></span></span>，如果不定义协程为非静态成员函数，
</li><li> <span class="t-c"><span class="mw-geshi cpp source-cpp">std<span class="sy4">::</span><span class="me2">coroutine_traits</span><span class="sy1">&lt;</span>R, ClassT <span class="coMULTI">/*cv限定*/</span><span class="sy3">&amp;</span>, Args...<span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">promise_type</span></span></span>，如果定义协程为非右值引用限定的非静态成员函数，
</li><li> <span class="t-c"><span class="mw-geshi cpp source-cpp">std<span class="sy4">::</span><span class="me2">coroutine_traits</span><span class="sy1">&lt;</span>R, ClassT <span class="coMULTI">/*cv限定*/</span><span class="sy3">&amp;&amp;</span>, Args...<span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">promise_type</span></span></span>，如果定义协程为右值引用限定的非静态成员函数。
</li></ul>
<p>例如：
</p>
<ul><li> 如果定义协程为 <span class="t-c"><span class="mw-geshi cpp source-cpp">task<span class="sy1">&lt;</span><span class="kw4">float</span><span class="sy1">&gt;</span> foo<span class="br0">(</span><a href="../string/basic_string.html"><span class="kw1230">std::<span class="me2">string</span></span></a> x, <span class="kw4">bool</span> flag<span class="br0">)</span><span class="sy4">;</span></span></span>，那么它的承诺类型是 <span class="t-c"><span class="mw-geshi cpp source-cpp">std<span class="sy4">::</span><span class="me2">coroutine_traits</span><span class="sy1">&lt;</span>task<span class="sy1">&lt;</span><span class="kw4">float</span><span class="sy1">&gt;</span>, <a href="../string/basic_string.html"><span class="kw1230">std::<span class="me2">string</span></span></a>, <span class="kw4">bool</span><span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">promise_type</span></span></span>。
</li><li> 如果定义协程为 <span class="t-c"><span class="mw-geshi cpp source-cpp">task<span class="sy1">&lt;</span><span class="kw4">void</span><span class="sy1">&gt;</span> my_class<span class="sy4">::</span><span class="me2">method1</span><span class="br0">(</span><span class="kw4">int</span> x<span class="br0">)</span> <span class="kw4">const</span><span class="sy4">;</span></span></span>，那么它的承诺类型是 <span class="t-c"><span class="mw-geshi cpp source-cpp">std<span class="sy4">::</span><span class="me2">coroutine_traits</span><span class="sy1">&lt;</span>task<span class="sy1">&lt;</span><span class="kw4">void</span><span class="sy1">&gt;</span>, <span class="kw4">const</span> my_class<span class="sy3">&amp;</span>, <span class="kw4">int</span><span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">promise_type</span></span></span>。
</li><li> 如果定义协程为 <span class="t-c"><span class="mw-geshi cpp source-cpp">task<span class="sy1">&lt;</span><span class="kw4">void</span><span class="sy1">&gt;</span> my_class<span class="sy4">::</span><span class="me2">method1</span><span class="br0">(</span><span class="kw4">int</span> x<span class="br0">)</span> <span class="sy3">&amp;&amp;</span><span class="sy4">;</span></span></span>，那么它的承诺类型是 <span class="t-c"><span class="mw-geshi cpp source-cpp">std<span class="sy4">::</span><span class="me2">coroutine_traits</span><span class="sy1">&lt;</span>task<span class="sy1">&lt;</span><span class="kw4">void</span><span class="sy1">&gt;</span>, my_class<span class="sy3">&amp;&amp;</span>, <span class="kw4">int</span><span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">promise_type</span></span></span>。
</li></ul>
<h3><span class="mw-headline" id="co_await"> co_await </span></h3>
<p>一元运算符 <span class="t-c"><span class="mw-geshi cpp source-cpp">co_await</span></span> 暂停协程并将控制返回给调用方。它的操作数是一个表达式，其类型必须要么定义 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator co_await</span></span>，要么能以当前协程的 <span class="t-c"><span class="mw-geshi cpp source-cpp">Promise<span class="sy4">::</span><span class="me2">await_transform</span></span></span> 转换到这种类型。
</p>
<table class="t-sdsc-begin">

<tr>
<td colspan="10" class="t-sdsc-sep">
</td></tr>
<tr class="t-sdsc">
<td class="t-sdsc-nopad"> <code><b>co_await</b></code> <span class="t-spar">表达式</span>
</td>
<td class="t-sdsc-nopad">
</td>
<td class="t-sdsc-nopad">
</td></tr>



<tr>
<td colspan="10" class="t-sdsc-sep">
</td></tr></table>
<p>首先，以下列方式将 <span class="t-spar">表达式</span> 转换成可等待体（awaitable）：
</p>
<ul><li> 如果 <span class="t-spar">表达式</span> 由初始暂停点、最终暂停点或 yield 表达式所产生，那么可等待体是 <span class="t-spar">表达式</span> 本身。
</li><li> 否则，如果当前协程的承诺类型拥有成员函数 <span class="t-c"><span class="mw-geshi cpp source-cpp">await_transform</span></span>，那么可等待体是 <span class="t-c"><span class="mw-geshi cpp source-cpp">promise.<span class="me1">await_transform</span><span class="br0">(</span>表达式<span class="br0">)</span></span></span>。
</li><li> 否则，可等待体是 <span class="t-spar">表达式</span> 本身。
</li></ul>
<p>然后以下列方式获得等待器（awaiter）对象：
</p>
<ul><li> 如果针对 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator co_await</span></span> 的重载决议给出单个最佳重载，那么等待器是该调用的结果（对于成员重载为 <span class="t-c"><span class="mw-geshi cpp source-cpp">awaitable.<span class="me1">operator</span> co_await<span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span></span></span>，对于非成员重载为 <span class="t-c"><span class="mw-geshi cpp source-cpp">operator co_await<span class="br0">(</span><span class="kw1">static_cast</span><span class="sy1">&lt;</span>Awaitable<span class="sy3">&amp;&amp;</span><span class="sy1">&gt;</span><span class="br0">(</span>awaitable<span class="br0">)</span><span class="br0">)</span><span class="sy4">;</span></span></span>）
</li><li> 否则，如果重载决议找不到 <code>operator co_await</code>，那么等待器是可等待体本身
</li><li> 否则，如果重载决议有歧义，那么程序非良构
</li></ul>
<p>如果上述表达式为纯右值，那么等待器对象是从它<a href="implicit_cast.html#.E4.B8.B4.E6.97.B6.E9.87.8F.E5.AE.9E.E8.B4.A8.E5.8C.96" title="cpp/language/implicit conversion">实质化</a>的临时量。否则，如果上述表达式为泛左值，那么等待器对象是它所指代的对象。
</p><p>然后，调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">awaiter.<span class="me1">await_ready</span><span class="br0">(</span><span class="br0">)</span></span></span>（这是当已知结果就绪或可以同步完成时，用以避免暂停开销的快捷方式）。如果它的结果按语境转换成 <code>bool</code> 为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">false</span></span></span>，那么：
</p>
<dl><dd> 暂停协程（以各局部变量和当前暂停点填充其协程状态）。
</dd><dd> 调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">awaiter.<span class="me1">await_suspend</span><span class="br0">(</span>handle<span class="br0">)</span></span></span>，其中 handle 是表示当前协程的协程句柄。这个函数内部可以通过这个句柄观察暂停的协程，而且此函数负责调度它以在某个执行器上恢复，或将其销毁（并返回 <code>false</code> 当做调度）
<ul><li> 如果 <code>await_suspend</code> 返回 <code>void</code>，那么立即将控制返回给当前协程的调用方/恢复方（此协程保持暂停），否则
</li><li> 如果 <code>await_suspend</code> 返回 <code>bool</code>，那么：
</li></ul>
<dl><dd><ul><li> 值为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">true</span></span></span> 时将控制返回给当前协程的调用方/恢复方
</li><li> 值为 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="kw2">false</span></span></span> 时恢复当前协程。
</li></ul>
</dd></dl>
<ul><li> 如果 <code>await_suspend</code> 返回某个其他协程的协程句柄，那么（通过调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">handle.<span class="me1">resume</span><span class="br0">(</span><span class="br0">)</span></span></span>）恢复该句柄（注意这可以连锁进行，并最终导致当前协程恢复）
</li><li> 如果 <code>await_suspend</code> 抛出异常，那么捕捉该异常，恢复协程，并立即重抛异常
</li></ul>
</dd><dd> 最后，调用 <span class="t-c"><span class="mw-geshi cpp source-cpp">awaiter.<span class="me1">await_resume</span><span class="br0">(</span><span class="br0">)</span></span></span>，它的结果就是整个 <span class="t-c"><span class="mw-geshi cpp source-cpp">co_await expr</span></span> 表达式的结果。
</dd></dl>
<p>如果协程在 co_await 表达式中暂停而在后来恢复，那么恢复点处于紧接对 <span class="t-c"><span class="mw-geshi cpp source-cpp">awaiter.<span class="me1">await_resume</span><span class="br0">(</span><span class="br0">)</span></span></span> 的调用之前。
</p><p>注意，因为协程在进入 <span class="t-c"><span class="mw-geshi cpp source-cpp">awaiter.<span class="me1">await_suspend</span><span class="br0">(</span><span class="br0">)</span></span></span> 前已经完全暂停，所以该函数可以自由地在线程间转移协程柄而无需额外同步。例如，可以将它放入回调，将它调度成在异步 I/O 操作完成时在线程池上运行等。此时因为当前协程可能已被恢复，从而执行了等待器的析构函数，同时由于 <code>await_suspend()</code> 在当前线程上持续执行， <code>await_suspend()</code> 应该把 <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="sy2">*</span>this</span></span> 当作已被销毁并且在柄被发布到其他线程后不再访问它。
</p>
<h3><span class="mw-headline" id=".E7.A4.BA.E4.BE.8B"> 示例 </span></h3>
<div class="t-example"><div class="t-example-live-link"><div class="coliru-btn coliru-btn-run-init">运行此代码</div></div>
<div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="cpp source-cpp"><pre class="de1"><span class="co2">#include &lt;coroutine&gt;</span>
<span class="co2">#include &lt;iostream&gt;</span>
<span class="co2">#include &lt;stdexcept&gt;</span>
<span class="co2">#include &lt;thread&gt;</span>
 
<span class="kw4">auto</span> switch_to_new_thread<span class="br0">(</span>std<span class="sy4">::</span><span class="me2">jthread</span><span class="sy3">&amp;</span> out<span class="br0">)</span> <span class="br0">{</span>
  <span class="kw1">struct</span> awaitable <span class="br0">{</span>
    std<span class="sy4">::</span><span class="me2">jthread</span><span class="sy2">*</span> p_out<span class="sy4">;</span>
    <span class="kw4">bool</span> await_ready<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="kw2">false</span><span class="sy4">;</span> <span class="br0">}</span>
    <span class="kw4">void</span> await_suspend<span class="br0">(</span>std<span class="sy4">::</span><span class="me2">coroutine_handle</span><span class="sy1">&lt;&gt;</span> h<span class="br0">)</span> <span class="br0">{</span>
      std<span class="sy4">::</span><span class="me2">jthread</span><span class="sy3">&amp;</span> out <span class="sy1">=</span> <span class="sy2">*</span>p_out<span class="sy4">;</span>
      <span class="kw1">if</span> <span class="br0">(</span>out.<span class="me1">joinable</span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span>
        <span class="kw1">throw</span> <a href="../error/runtime_error.html"><span class="kw770">std::<span class="me2">runtime_error</span></span></a><span class="br0">(</span><span class="st0">"jthread 输出参数非空"</span><span class="br0">)</span><span class="sy4">;</span>
      out <span class="sy1">=</span> std<span class="sy4">::</span><span class="me2">jthread</span><span class="br0">(</span><span class="br0">[</span>h<span class="br0">]</span> <span class="br0">{</span> h.<span class="me1">resume</span><span class="br0">(</span><span class="br0">)</span><span class="sy4">;</span> <span class="br0">}</span><span class="br0">)</span><span class="sy4">;</span>
      <span class="co1">// 潜在的未定义行为：访问潜在被销毁的 *this</span>
      <span class="co1">// std::cout &lt;&lt; "新线程 ID：" &lt;&lt; p_out-&gt;get_id() &lt;&lt; "\n";</span>
      <a href="../io/cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1">&lt;&lt;</span> <span class="st0">"新线程 ID："</span> <span class="sy1">&lt;&lt;</span> out.<span class="me1">get_id</span><span class="br0">(</span><span class="br0">)</span> <span class="sy1">&lt;&lt;</span> <span class="st0">"<span class="es1">\n</span>"</span><span class="sy4">;</span> <span class="co1">// 这样可以</span>
    <span class="br0">}</span>
    <span class="kw4">void</span> await_resume<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span><span class="br0">}</span>
  <span class="br0">}</span><span class="sy4">;</span>
  <span class="kw1">return</span> awaitable<span class="br0">{</span><span class="sy3">&amp;</span>out<span class="br0">}</span><span class="sy4">;</span>
<span class="br0">}</span>
 
<span class="kw1">struct</span> task <span class="br0">{</span>
    <span class="kw1">struct</span> promise_type <span class="br0">{</span>
        task get_return_object<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span> <span class="br0">}</span>
        std<span class="sy4">::</span><span class="me2">suspend_never</span> initial_suspend<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span> <span class="br0">}</span>
        std<span class="sy4">::</span><span class="me2">suspend_never</span> final_suspend<span class="br0">(</span><span class="br0">)</span> <span class="kw1">noexcept</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="br0">{</span><span class="br0">}</span><span class="sy4">;</span> <span class="br0">}</span>
        <span class="kw4">void</span> return_void<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span><span class="br0">}</span>
        <span class="kw4">void</span> unhandled_exception<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span><span class="br0">}</span>
    <span class="br0">}</span><span class="sy4">;</span>
<span class="br0">}</span><span class="sy4">;</span>
 
task resuming_on_new_thread<span class="br0">(</span>std<span class="sy4">::</span><span class="me2">jthread</span><span class="sy3">&amp;</span> out<span class="br0">)</span> <span class="br0">{</span>
  <a href="../io/cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1">&lt;&lt;</span> <span class="st0">"协程开始，线程 ID："</span> <span class="sy1">&lt;&lt;</span> <a href="../thread/get_id.html"><span class="kw2148">std::<span class="me2">this_thread</span><span class="sy4">::</span><span class="me2">get_id</span></span></a><span class="br0">(</span><span class="br0">)</span> <span class="sy1">&lt;&lt;</span> <span class="st0">"<span class="es1">\n</span>"</span><span class="sy4">;</span>
  co_await switch_to_new_thread<span class="br0">(</span>out<span class="br0">)</span><span class="sy4">;</span>
  <span class="co1">// 等待器在此销毁</span>
  <a href="../io/cout.html"><span class="kw1758">std::<span class="me2">cout</span></span></a> <span class="sy1">&lt;&lt;</span> <span class="st0">"协程恢复，线程 ID："</span> <span class="sy1">&lt;&lt;</span> <a href="../thread/get_id.html"><span class="kw2148">std::<span class="me2">this_thread</span><span class="sy4">::</span><span class="me2">get_id</span></span></a><span class="br0">(</span><span class="br0">)</span> <span class="sy1">&lt;&lt;</span> <span class="st0">"<span class="es1">\n</span>"</span><span class="sy4">;</span>
<span class="br0">}</span>
 
<span class="kw4">int</span> main<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
  std<span class="sy4">::</span><span class="me2">jthread</span> out<span class="sy4">;</span>
  resuming_on_new_thread<span class="br0">(</span>out<span class="br0">)</span><span class="sy4">;</span>
<span class="br0">}</span></pre></div></div>
<p>可能的输出：
</p>
<div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="text source-text"><pre class="de1">协程开始，线程 ID：139972277602112
新线程 ID：139972267284224
协程恢复，线程 ID：139972267284224</pre></div></div> 
</div>
<p>注意：等待器对象是协程状态的一部分（作为生存期跨过暂停点的临时量），并且在 co_await 表达式结束前销毁。可以用它维护某些异步 I/O API 所要求的每操作内状态，而无需用到额外的堆分配。
</p><p>标准库定义了两个平凡的可等待体：<span class="t-lc">std::suspend_always</span> 及 <span class="t-lc">std::suspend_never</span>。
</p>
<table class="metadata plainlinks ambox mbox-small-left ambox-notice" style=""><tr><td class="mbox-empty-cell"></td><td class="mbox-text" style="">本节未完成<br>原因：示例 </td></tr></table>
<h3><span class="mw-headline" id="co_yield"> co_yield </span></h3>
<p>yield 表达式向调用方返回一个值并暂停当前协程：它是可恢复生成器函数的常用构建块
</p>
<table class="t-sdsc-begin">

<tr>
<td colspan="10" class="t-sdsc-sep">
</td></tr>
<tr class="t-sdsc">
<td class="t-sdsc-nopad"> <code><b>co_yield</b></code> <span class="t-spar">表达式</span>
</td>
<td class="t-sdsc-nopad">
</td>
<td class="t-sdsc-nopad">
</td></tr>



<tr>
<td colspan="10" class="t-sdsc-sep">
</td></tr>
<tr class="t-sdsc">
<td class="t-sdsc-nopad"> <code><b>co_yield</b></code> <span class="t-spar">花括号初始化器列表</span>
</td>
<td class="t-sdsc-nopad">
</td>
<td class="t-sdsc-nopad">
</td></tr>



<tr>
<td colspan="10" class="t-sdsc-sep">
</td></tr></table>
<p>它等价于
</p>
<div dir="ltr" class="mw-geshi" style="text-align: left;"><div class="cpp source-cpp"><pre class="de1">co_await promise.<span class="me1">yield_value</span><span class="br0">(</span>表达式<span class="br0">)</span></pre></div></div>
<p>典型的生成器的 <code>yield_value</code> 会将其实参存储（复制/移动或仅存储其地址，因为实参的生存期跨过 co_await 内的暂停点）到生成器对象中并返回 <span class="t-lc">std::suspend_always</span>，将控制转移给调用方/恢复方。
</p>
<table class="metadata plainlinks ambox mbox-small-left ambox-notice" style=""><tr><td class="mbox-empty-cell"></td><td class="mbox-text" style="">本节未完成<br>原因：示例 </td></tr></table>
<h3><span class="mw-headline" id=".E5.BA.93.E6.94.AF.E6.8C.81"> 库支持 </span></h3>
<p><a href="../coroutine.html" title="cpp/coroutine">协程支持库</a>定义数个类型，提供协程的编译与运行时支持。
</p>
<!-- 
NewPP limit report
Preprocessor visited node count: 3149/1000000
Preprocessor generated node count: 8360/1000000
Post‐expand include size: 44907/2097152 bytes
Template argument size: 11900/2097152 bytes
Highest expansion depth: 17/40
Expensive parser function count: 0/100
-->

<!-- Saved in parser cache with key mwiki1-mwiki_zh_:pcache:idhash:12015-0!*!0!!zh!2!zh!* and timestamp 20211022194140 -->
</div>                    <!-- /bodycontent -->
                                        <!-- printfooter -->
                    <div class="printfooter">
                    来自“<a href="https://zh.cppreference.com/mwiki/index.php?title=cpp/language/coroutines&amp;oldid=71917">https://zh.cppreference.com/mwiki/index.php?title=cpp/language/coroutines&amp;oldid=71917</a>”                    </div>
                    <!-- /printfooter -->
                                                            <!-- catlinks -->
                    <!-- /catlinks -->
                                                            <div class="visualClear"></div>
                    <!-- debughtml -->
                                        <!-- /debughtml -->
                </div>
                <!-- /bodyContent -->
            </div>
        </div>
        <!-- /content -->
        <!-- footer -->
        <div id="cpp-footer-base" class="noprint">
            <div id="footer">
                        <div id="cpp-navigation">
            <h5>导航</h5>
            <ul><li><a href="https://zh.cppreference.com/w/cpp/language/coroutines">Online version</a></li><li>Offline version retrieved 2022-01-01 00:37.</li></ul></div>
                        <ul id="footer-info">
                                    <li id="footer-info-lastmod"> 本页面最后修改于2021年8月18日 (星期三) 19:41。</li>
                                    <li id="footer-info-viewcount">此页面已被浏览过19,402次。</li>
                            </ul>
                    </div>
        </div>
        <!-- /footer -->
        <script>if(window.mw){
mw.loader.state({"site":"loading","user":"missing","user.groups":"ready"});
}</script>
<script src="../../../common/skin_scripts.js"></script>
<script>if(window.mw){
mw.loader.load(["mediawiki.action.view.postEdit","mediawiki.user","mediawiki.page.ready","mediawiki.searchSuggest","mediawiki.hidpi","ext.gadget.MathJax","ext.gadget.ColiruCompiler"], null, true);
}</script>
<script src="../../../common/site_scripts.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-2828341-1']);
_gaq.push(['_setDomainName', 'cppreference.com']);
_gaq.push(['_trackPageview']);
</script><!-- Served in 3.895 secs. -->
	</body>
<!-- Cached 20211022194140 -->
</html>